﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Diagnostics;

using Logging;

namespace SGGWElevator.Controllers
{
    /// <summary>
    /// Conatiner for requests
    /// Author: Paweł Iżycki
    /// </summary>
    public class RequestsController : ControllerBase
    {
        public event PendingRequestReloadedEventHandler PendingRequestReloaded;
        public event NewPendingRequestAddedEventHandler NewPendingRequestAdded;
        public delegate void PendingRequestReloadedEventHandler(object sender, Request pendingRequest);
        public delegate void NewPendingRequestAddedEventHandler(object sender, Request pendingRequest);

        /// <summary>
        /// Instance of elevator controller
        /// </summary>
        ElevatorController _elevatorController;

        /// <summary>
        /// List of pending requests.
        /// </summary>
        List<Request> pendingRequestsList;

        // Constructor
        public RequestsController(ElevatorController elevatorController)
        {
            pendingRequestsList = new List<Request>();
            _elevatorController = elevatorController;
            _elevatorController.PerformingRequestEnded += _elevatorController_PerformingRequestEnded;
        }

        void _elevatorController_PerformingRequestEnded(object sender, Request finishedRequest)
        {
            ReloadPendingRequest();
        }

        /// <summary>
        /// Adds request to the container.
        /// </summary>
        /// <param name="addedRequest">Request to add.</param>
        public void Add(Request addedRequest)
        {
            if (addedRequest == null)
                throw new ArgumentNullException();

            lock (pendingRequestsList)
            {
                _logger.WriteLine("ReuqestController.Add - pendingRequestsList locked!");

                // If the same looking or identicall request exists in the request list, skip adding request.
                if (pendingRequestsList.Exists(x => addedRequest == x))
                {
                    System.Diagnostics.Debug.WriteLine("ReuqestController.Add - The same (or very similiar) request already exists in the pending requests list, adding request will be skipped.");
                    return;
                }

                #region Adding new pending request algorithm

                _logger.WriteLine("Request to be added: " + addedRequest.ToString());

#if true // podejście #1

                bool addingSuccessed = false;

                if (pendingRequestsList.Count != 0)
                {
                    Direction rideDirection = pendingRequestsList[0].Direction;

                    foreach (Request request in pendingRequestsList)
                    {

                        if ((addedRequest.RequestedFloor.IsBefore(request.RequestedFloor) && addedRequest.Direction == Direction.Up)
                            || (addedRequest.RequestedFloor.IsAfter(request.RequestedFloor) && addedRequest.Direction == Direction.Down))
                        {
                            pendingRequestsList.Insert(pendingRequestsList.IndexOf(request), addedRequest);
                            addingSuccessed = true;
                            return;
                        }

                    }
                }

                if (!addingSuccessed)
                {
                    pendingRequestsList.Add(addedRequest);
                    addingSuccessed = true;
                }

                if (addingSuccessed)
                {
                    // Raise event
                    if (NewPendingRequestAdded != null)
                        NewPendingRequestAdded(this, addedRequest);
                }

#endif


#if false // podejście #2
                lock (pendingRequestsList)
                {
                    bool addedSuccessfuly = false;

                    if (pendingRequestsList.Count != 0)
                    {
                        Direction actualRideDirection = _elevatorController.RideDirection;

                        _logger.WriteLine("=========" + actualRideDirection.ToString());

                        List<Request> sameDirectionRequests = pendingRequestsList.TakeWhile(request => request.Direction == actualRideDirection).ToList();
                        List<Request> oppositeDirectionRequests = pendingRequestsList.TakeWhile(request => request.Direction != actualRideDirection).ToList();

                        List<Request> requestsToIterate;

                        if (actualRideDirection == addedRequest.Direction)
                            requestsToIterate = sameDirectionRequests;
                        else
                            requestsToIterate = oppositeDirectionRequests;

                        foreach (Request request in requestsToIterate)
                        {
                            if ((request.Direction == Direction.Down && request.RequestedFloor.IsBefore(request.RequestedFloor)) || (request.Direction == Direction.Up && request.RequestedFloor.IsAfter(request.RequestedFloor)))
                            {
                                requestsToIterate.Insert(requestsToIterate.IndexOf(request), addedRequest);
                                addedSuccessfuly = true;
                                return;
                            }
                        }

                        if (!addedSuccessfuly)
                        {
                            requestsToIterate.Add(addedRequest);
                            addedSuccessfuly = true;
                        }

                        pendingRequestsList = sameDirectionRequests.Concat(oppositeDirectionRequests).ToList();
                    }
                    else
                    {
                        pendingRequestsList.Add(addedRequest);
                        addedSuccessfuly = true;
                    }
                    if (addedSuccessfuly)
                    {
                        // Raise event
                        if (NewPendingRequestAdded != null)
                            NewPendingRequestAdded(this, addedRequest);
                    }
                }

#endif


                #endregion



                _logger.WriteLine("ReuqestController.Add - All pending floors:");
                _logger.WriteLine(GetAllPendingFloors());

                // Check is controller busy performing request
                if (!_elevatorController.IsPerformingRequest)
                    ReloadPendingRequest();

                _logger.WriteLine("ReuqestController.Add - Releasing pendingRequestsList...");
            }
        }

        string GetAllPendingFloors()
        {
            string result = string.Empty;

            if (this.pendingRequestsList.Count == 0)
                return "GetAllPendingFloors - No floors pending.";
            else
                foreach (var floor in this.pendingRequestsList)
                    result += floor.RequestedFloor.ToString() + ", ";

            return result;
        }

        /// <summary>
        /// Gets first request in the list and sets as a Pending Request.
        /// Loaded request will be lost.
        /// </summary>
        public void ReloadPendingRequest()
        {
            lock (pendingRequestsList)
            {
                if (pendingRequestsList.Count == 0)
                    return;

                _logger.WriteLine("Reloading pending request...");
                Request pendingRequest = pendingRequestsList[0];
                pendingRequestsList.Remove(pendingRequest);

                // Raise event
                if (PendingRequestReloaded != null)
                    PendingRequestReloaded(this, pendingRequest);

                _logger.WriteLine("Pending request reloaded!");
            }
        }

    }
}
